package com.doudou.collection.domain.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.doudou.collection.dao.mapper.UserDetailMapper;
import com.doudou.collection.dao.mapper.UserMapper;
import com.doudou.collection.domain.service.UserService;
import com.doudou.collection.infrastructure.common.ex.ServiceException;
import com.doudou.collection.infrastructure.common.web.StatusCode;
import com.doudou.collection.infrastructure.core.security.CustomUserDetails;
import com.doudou.collection.infrastructure.core.service.TokenService;
import com.doudou.collection.orm.dto.user.login.*;
import com.doudou.collection.orm.dto.user.reg.*;
import com.doudou.collection.orm.dto.user.update.*;
import com.doudou.collection.orm.entity.User;
import com.doudou.collection.orm.entity.UserDetail;
import com.doudou.collection.orm.vo.UserLoginSimpleVO;
import com.doudou.collection.orm.vo.UserLoginVO;
import com.doudou.collection.utils.generator.UsernameGenerator;
import com.doudou.collection.utils.redis.RedisConstants;
import com.doudou.collection.utils.regex.RegexUtils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName UserServiceImpl<br>
 * @Description 用户业务逻辑类实现类<br>
 * @Author 李浩文<br>
 * @Date 2023/6/2 17:43<br>
 * @Version 1.0<br>
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserDetailMapper userDetailMapper;

    @Autowired
    private AuthenticationManager manager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void addNewByUsername(UserRegDTO userRegDTO) {
        String username = userRegDTO.getUsername();
        String password = userRegDTO.getPassword();
        log.debug("正在处理用户注册业务功能: 账号-->{} 密码-->{}", username, password);

        //查询用户是否存在
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.eq("username", username);
        Integer count = userMapper.selectCount(qw);
        if (count > 0) {
            log.error("用户已存在！");
            throw new ServiceException(StatusCode.USERNAME_ALREADY_EXISTS);
        }

        //插入用户信息
        User user = new User();
        user.setUsername(username);
        user.setIcon("/douIcon.png");
        user.setPassword(passwordEncoder.encode(password));
        userMapper.insert(user);

        //插入用户详情信息
        UserDetail userDetail = new UserDetail();
        userDetail.setUserId(user.getId());
        userDetailMapper.insert(userDetail);

    }

    @Override
    public void addNewByEmail(EmailRegDTO emailRegDTO) {
        String email = emailRegDTO.getEmail();
        String password = emailRegDTO.getPassword();
        log.debug("正在处理邮箱注册业务功能: 邮箱-->{} 密码-->{}", email, password);

        //查询用户是否存在
        QueryWrapper<UserDetail> qw = new QueryWrapper<>();
        qw.eq("email", email);
        Integer count = userDetailMapper.selectCount(qw);
        if (count > 0) {
            log.error("邮箱已存在！");
            throw new ServiceException(StatusCode.EMAIL_ALREADY_EXISTS);
        }

        //插入用户信息
        User user = new User();
        //自动生成账户的用户名
        user.setUsername(UsernameGenerator.generateUsername("DDJ"));
        user.setIcon("/douIcon.png");
        user.setPassword(passwordEncoder.encode(password));
        userMapper.insert(user);

        //插入用户详情信息
        UserDetail userDetail = new UserDetail();
        userDetail.setUserId(user.getId());
        userDetail.setEmail(email);
        userDetailMapper.insert(userDetail);

    }

    @Override
    public void addNewByPhone(PhoneRegDTO phoneRegDTO) {
        String phone = phoneRegDTO.getPhone();
        String password = phoneRegDTO.getPassword();
        log.debug("正在处理手机注册业务功能: 手机-->{} 密码-->{}", phone, password);

        //查询用户是否存在
        QueryWrapper<UserDetail> qw = new QueryWrapper<>();
        qw.eq("phone", phone);
        Integer count = userDetailMapper.selectCount(qw);
        if (count > 0) {
            log.error("手机号已存在！");
            throw new ServiceException(StatusCode.PHONE_ALREADY_EXISTS);
        }

        //插入用户信息
        User user = new User();
        //自动生成账户的用户名
        user.setUsername(UsernameGenerator.generateUsername("DDJ"));
        user.setIcon("/douIcon.png");
        user.setPassword(passwordEncoder.encode(password));
        userMapper.insert(user);

        //插入用户详情信息
        UserDetail userDetail = new UserDetail();
        userDetail.setUserId(user.getId());
        userDetail.setPhone(phone);
        userDetailMapper.insert(userDetail);

    }

    @Override
    public void addNewByPhoneAndCode(PhoneCodeRegDTO phoneCodeRegDTO) {
        String phone = phoneCodeRegDTO.getPhone();
        String code = phoneCodeRegDTO.getCode();
        log.debug("正在处理手机验证码注册业务功能: 手机-->{} 验证码-->{}", phone, code);

        //判断验证码是否正确
        String phoneCode = redisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);
        if (!code.equals(phoneCode)) {
            log.error("手机验证码错误!");
            throw new ServiceException(StatusCode.CODE_ERROR);
        }

        //查询用户是否存在
        QueryWrapper<UserDetail> qw = new QueryWrapper<>();
        qw.eq("phone", phone);
        Integer count = userDetailMapper.selectCount(qw);
        if (count > 0) {
            log.error("手机号已存在！");
            throw new ServiceException(StatusCode.PHONE_ALREADY_EXISTS);
        }

        //插入用户信息
        User user = new User();
        //自动生成账户的用户名
        user.setUsername(UsernameGenerator.generateUsername("DDJ"));
        user.setIcon("/douIcon.png");
        userMapper.insert(user);

        //插入用户详情信息
        UserDetail userDetail = new UserDetail();
        userDetail.setUserId(user.getId());
        userDetail.setPhone(phone);
        userDetailMapper.insert(userDetail);

    }

    @Override
    public void addNewByEmailAndCode(EmailCodeRegDTO emailCodeRegDTO) {
        String email = emailCodeRegDTO.getEmail();
        String code = emailCodeRegDTO.getCode();
        log.debug("正在处理手机验证码注册业务功能: 邮箱-->{} 验证码-->{}", email, code);


        //判断验证码是否正确
        String emailCode = redisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + email);
        if (!code.equals(emailCode)) {
            log.error("邮箱验证码错误!");
            throw new ServiceException(StatusCode.CODE_ERROR);
        }

        //查询用户是否存在
        QueryWrapper<UserDetail> qw = new QueryWrapper<>();
        qw.eq("email", email);
        Integer count = userDetailMapper.selectCount(qw);
        if (count > 0) {
            log.error("邮箱已存在！");
            throw new ServiceException(StatusCode.EMAIL_ALREADY_EXISTS);
        }

        //插入用户信息
        User user = new User();
        //自动生成账户的用户名
        user.setUsername(UsernameGenerator.generateUsername("DDJ"));
        user.setIcon("/douIcon.png");
        userMapper.insert(user);

        //插入用户详情信息
        UserDetail userDetail = new UserDetail();
        userDetail.setUserId(user.getId());
        userDetail.setEmail(email);
        userDetailMapper.insert(userDetail);

    }

    @Autowired
    private TokenService tokenService;

    @Override
    public UserLoginSimpleVO loginByUsername(UserLoginDTO userLoginDTO) {
        String username = userLoginDTO.getUsername();
        String password = userLoginDTO.getPassword();
        log.debug("正在处理用户名登录业务功能: {} {}", username, password);

        return authentication(username, password);
    }

    @Override
    public UserLoginSimpleVO loginByEmail(EmailLoginDTO emailLoginDTO) {
        String email = emailLoginDTO.getEmail();
        String password = emailLoginDTO.getPassword();
        log.debug("正在处理邮箱登录业务功能: {} {}", email, password);

        return authentication(email, password);
    }

    @Override
    public UserLoginSimpleVO loginByPhone(PhoneLoginDTO phoneLoginDTO) {
        String phone = phoneLoginDTO.getPhone();
        String password = phoneLoginDTO.getPassword();
        log.debug("正在处理手机号登录业务功能: {} {}", phone, password);

        return authentication(phone, password);
    }

    @Override
    public UserLoginSimpleVO loginByPhoneAndCode(PhoneCodeLoginDTO phoneCodeLoginDTO) {
        String phone = phoneCodeLoginDTO.getPhone();
        String code = phoneCodeLoginDTO.getCode();
        log.debug("正在处理手机号登录业务功能: {} {}", phone, code);

        UserLoginVO userVO = userMapper.queryByPhone(phone);
        if (userVO == null) {
            //根据手机号码没有查询到指定用户
            log.error("没有查询到该用户！ ===> {}", phone);
            throw new ServiceException(StatusCode.PHONE_ERROR);
        }
        if (!code.equals(redisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone))) {
            //验证码错误 验证失败
            log.error("验证码错误！ ===> {}", code);
            throw new ServiceException(StatusCode.CODE_ERROR);
        }

        return authentication(userVO);

    }

    @Override
    public UserLoginSimpleVO loginByEmailAndCode(EmailCodeLoginDTO emailCodeLoginDTO) {
        String email = emailCodeLoginDTO.getEmail();
        String code = emailCodeLoginDTO.getCode();
        log.debug("正在处理手机号登录业务功能: {} {}", email, code);

        UserLoginVO userDetailVO = userMapper.queryByEmail(email);
        if (userDetailVO == null) {
            //根据邮箱没有查询到指定用户
            log.error("没有查询到该用户！ ===> {}", email);
            throw new ServiceException(StatusCode.EMAIL_ERROR);
        }
        String redisEmailCode = redisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + email);
        if (!code.equals(redisEmailCode)) {
            //验证码错误 验证失败
            log.error("验证码错误！ ===> {}", code);
            throw new ServiceException(StatusCode.CODE_ERROR);
        }

        return authentication(userDetailVO);
    }

    private UserLoginSimpleVO authentication(UserLoginVO userVO) {
        List<String> permissions = userVO.getPermissions();
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String permission : permissions) {
            GrantedAuthority authority = new SimpleGrantedAuthority(permission);
            authorities.add(authority);
        }
        CustomUserDetails userDetails = new CustomUserDetails(userVO, authorities);
        String token = tokenService.createToken(userDetails);

        return new UserLoginSimpleVO()
                .setId(userDetails.getId())
                .setUsername(userDetails.getUsername())
                .setIcon(userDetails.getIcon())
                .setToken(token);
    }

    private UserLoginSimpleVO authentication(String username, String password) {
        log.debug("准备调用AuthenticationManager的认证方法，判断此用户名、密码是否可以成功登录……");
        Authentication result = manager.authenticate(
                new UsernamePasswordAuthenticationToken(username, password)
        );
        log.debug("验证用户登录成功，返回的认证结果：{}", result);

        CustomUserDetails userDetails = (CustomUserDetails) result.getPrincipal();
        String token = tokenService.createToken(userDetails);

        return new UserLoginSimpleVO()
                .setId(userDetails.getId())
                .setUsername(userDetails.getUsername())
                .setIcon(userDetails.getIcon())
                .setToken(token);
    }

    @Override
    public void updateUserDetailById(UserDetailDTO userDetailDTO) {
        log.debug("正在处理用户修改个人信息的业务逻辑");

        UserDetail userDetail = new UserDetail();
        BeanUtils.copyProperties(userDetailDTO, userDetail);


        log.debug("-----------{}",userDetail);

       /* if (userDetailDTO.getEmail()!=null){
            userDetail.setEmail(userDetailDTO.getEmail());
        }
        log.debug("userDetailDTO.getEmail:{}",userDetailDTO.getEmail());
        if (userDetailDTO.getCity()!=null){
            userDetail.setCity(userDetailDTO.getCity());
        }
        log.debug("11111111");
        if (userDetailDTO.getGender()!=null){
            userDetail.setGender(userDetailDTO.getGender());
        }
        log.debug("222222222");
        if (userDetailDTO.getIntroduce()!=null){
            userDetail.setIntroduce(userDetailDTO.getIntroduce());
        }
        if (userDetailDTO.getBirthday()!=null){
            userDetail.setBirthday(userDetailDTO.getBirthday());
        }
        if (userDetailDTO.getJob()!=null){
            userDetail.setJob(userDetailDTO.getJob());
        }
        if (userDetailDTO.getSchool()!=null){
            userDetail.setSchool(userDetailDTO.getSchool());
        }*/

        //当前时间
        Date currentDateTime = new Date();

        //用户填写出生日期
        Date birthday = userDetailDTO.getBirthday();

        // 将 Date 转换为 LocalDate
        LocalDate currentDateTime1 = currentDateTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate birthday1 = birthday.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        Period period = Period.between(birthday1, currentDateTime1);

        userDetail.setAge(period.getYears());
        userDetail.setUserId(userDetailDTO.getUserId());

        int row = userDetailMapper.update(userDetail);
        log.debug("改完了改完了:{}",userDetail);
        if (row != 1) {
            String message = "修改用户详情失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_INSERT.getCode(), message);
        }

        User user = new User();
        if (userDetailDTO.getIconUrl()!=null){
            user.setIcon(userDetailDTO.getIconUrl());
            user.setId(userDetailDTO.getUserId());
        }
        log.debug("user:{}",user);

        row = userMapper.updateById(user);

        if (row != 1) {
            String message = "修改用户头像失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_INSERT.getCode(), message);
        }
    }

    @Override
    public void sendCodeToPhone(String phone) {
        log.debug("正在处理发送手机验证码的业务功能 phone--->{}", phone);

        //1.检验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2.不符合返回错误信息
            log.error("手机号不符合规则！ ===> {}", phone);
            throw new ServiceException(StatusCode.PHONE_FORMAT_ERROR);
        }

        //3.符合生成验证码
        String code = RandomUtil.randomNumbers(6);

        //4.保存到redis
        redisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone, code,
                RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);

        //5.发送验证码(模拟)
        log.info("验证码 ---> {}", code);
    }

    @Override
    public void sendCodeToEmail(String email) {
        log.debug("正在处理发送邮箱验证码的业务功能");

        //1.检验手机号
        if (RegexUtils.isEmailInvalid(email)) {
            //2.不符合返回错误信息
            log.error("邮箱不符合规则！ ===> {}", email);
            throw new ServiceException(StatusCode.EMAIL_FORMAT_ERROR);
        }

        //3.符合生成验证码
        String code = RandomUtil.randomNumbers(6);

        //4.保存到redis
        redisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + email, code,
                RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);

        //5.发送验证码(模拟)
        log.info("验证码 ---> {}", code);
    }

    @Override
    public void updateUsernameById(UserUpdateNameDTO userUpdateNameDTO) {
        log.debug("正在处理修改用户名的业务逻辑");

        if (RegexUtils.isUsernameInvalid(userUpdateNameDTO.getUsername())) {
            log.error("该用户名格式错误！ ===> {}", userUpdateNameDTO.getUsername());
            throw new ServiceException(StatusCode.USERNAME_FORMAT_ERROR);
        }

        UserLoginVO userVO = userMapper.queryByUsername(userUpdateNameDTO.getUsername());
        if (userVO != null) {
            log.error("用户名一致,不需要修改！");
            throw new ServiceException(StatusCode.SAME_USERNAME);
        }

        int row = userMapper.updateUsernameById(userUpdateNameDTO);
        if (row != 1) {
            String message = "修改用户失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_UPDATE.getCode(), message);
        }

    }

    @Override
    public void updatePasswordById(UserUpdatePwdDTO userUpdatePwdDTO) {
        log.debug("正在处理修改密码的业务逻辑");

        if (RegexUtils.isPasswordInvalid(userUpdatePwdDTO.getPassword())) {
            log.error("该密码格式错误！ ===> {}", userUpdatePwdDTO.getPassword());
            throw new ServiceException(StatusCode.PASSWORD_FORMAT_ERROR);
        }

        // 将需要修改的密码加密，以便和旧密码做对比
        String newPassword = passwordEncoder.encode(userUpdatePwdDTO.getPassword());
        String oldPassword = userMapper.queryPasswordById(userUpdatePwdDTO.getId());
        if (newPassword.equals(oldPassword)) {
            log.warn("密码一致,不需要修改！");
            throw new ServiceException(StatusCode.SAME_PASSWORD);
        }

        userUpdatePwdDTO.setPassword(newPassword);
        int row = userMapper.updatePasswordById(userUpdatePwdDTO);
        if (row != 1) {
            String message = "修改密码失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_UPDATE.getCode(), message);
        }
    }

    @Override
    public void updateEmailById(UserUpdateEmailDTO userUpdateEmailDTO) {
        log.debug("正在处理修改邮箱的业务逻辑");

        if (RegexUtils.isEmailInvalid(userUpdateEmailDTO.getEmail())) {
            log.error("该邮箱格式错误！ ===> {}", userUpdateEmailDTO.getEmail());
            throw new ServiceException(StatusCode.EMAIL_FORMAT_ERROR);
        }

        UserLoginVO userVO = userMapper.queryByEmail(userUpdateEmailDTO.getEmail());
        if (userVO != null) {
            log.warn("邮箱一致,不需要修改！");
            throw new ServiceException(StatusCode.SAME_EMAIL);
        }

        int row = userMapper.updateEmailById(userUpdateEmailDTO);
        if (row != 1) {
            String message = "修改邮箱失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_UPDATE.getCode(), message);
        }
    }

    @Override
    public void updatePhoneById(UserUpdatePhoneDTO userUpdatePhoneDTO) {
        log.debug("正在处理修改手机号的业务逻辑");

        if (RegexUtils.isPhoneInvalid(userUpdatePhoneDTO.getPhone())) {
            log.error("该手机号格式错误！ ===> {}", userUpdatePhoneDTO.getPhone());
            throw new ServiceException(StatusCode.PHONE_FORMAT_ERROR);
        }

        UserLoginVO userVO = userMapper.queryByPhone(userUpdatePhoneDTO.getPhone());
        if (userVO != null) {
            log.warn("手机号一致,不需要修改！");
            throw new ServiceException(StatusCode.SAME_PASSWORD);
        }

        int row = userMapper.updatePhoneById(userUpdatePhoneDTO);
        if (row != 1) {
            String message = "修改手机号失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_UPDATE.getCode(), message);
        }
    }
}









